using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics.Contracts;

namespace ReactiveLang
{
    public abstract class ReactiveHandle : IDisposable
    {
        IDisposable _handle;

        public ReactiveHost Host { get; private set; }

        public abstract bool Enabled { get; set; }

        internal ReactiveHandle(ReactiveHost host)
        {
            Contract.Requires(host != null);
            Host = host;
            _handle = host.AddHandle(this);
        }

        public virtual void Dispose()
        {
            _handle.Dispose();
        }

        public event EventHandler<ReactiveUnhandledExceptionEventArgs> UnhandledException;
        internal void OnException(ReactiveUnhandledExceptionEventArgs e)
        {
            var handler = UnhandledException;
            if (handler != null)
                handler(this, e);
            Host.OnException(e);
        }
        internal void OnException(Exception exception)
        {
            var e = new ReactiveUnhandledExceptionEventArgs(exception);
            OnException(e);
            if (!e.IsHandled)
                throw new ReactiveCodeException(exception);
        }
    }

    public sealed class ReactiveHandle<T> : ReactiveHandle
    {
        Expression.LatestValue<T> _value;
        public IExpression<T> Value
        {
            get { return _value; }
        }

        Variable<bool> _enabled = new Variable<bool>();
        public override bool Enabled
        {
            get { return _enabled.Value; }
            set { _enabled.Value = value; }
        }

        internal ReactiveHandle(ReactiveHost host, IExpression<T> expression)
            : base(host)
        {
            Contract.Requires<ArgumentNullException>(expression != null, "expression");

            _value = expression.If(_enabled).Latest(OnException);
        }
        public override void Dispose()
        {
            _value.Dispose();
            base.Dispose();
        }
    }
}